.js window.loadedScripts = {}; .js function loadScript(sources) { if (sources in window.loadedScripts) return; window.loadedScripts[sources] = true; var script = document.createElement('script'); script.src = sources; script.async = false; document.body.appendChild(script); const promise = new Deferred(); script.addEventListener('load', () => { promise.resolve(); }); return promise.promise; } core.ForceGraph3D = async (args, env) => { const data = await interpretate(args[0], env); await loadScript('https://unpkg.com/3d-force-graph'); await interpretate.shared.SpriteText.load(); const SpriteText = interpretate.shared.SpriteText.SpriteText; const opts = await core._getRules(args, env); // Build labels mapping const labels = (opts.VertexLabels || []).reduce((acc, { lhs, rhs }) => { acc[lhs] = rhs; return acc; }, {}); // Collect node IDs and construct links const nodeIds = new Set(); const links = data.map(({ lhs, rhs }) => { nodeIds.add(lhs); nodeIds.add(rhs); return { source: String(rhs), target: String(lhs) }; }); // Create nodes with labels const nodes = Array.from(nodeIds).map(id => ({ id: String(id), label: labels[id] || String(id), })); // Initialize the 3D force graph const Graph = ForceGraph3D()(env.element) .graphData({ nodes, links }) .nodeThreeObject(node => { const sprite = new SpriteText(node.label); sprite.material.depthWrite = true; // Make sprite background transparent sprite.color = 'white'; sprite.textHeight = 8; return sprite; }) .nodeThreeObjectExtend(false); // Apply optional charge strength if ('Charge' in opts) { Graph.d3Force('charge').strength(opts.Charge); } env.local.Graph = Graph; }; core.ForceGraph3D.destroy = () => { } core.ForceGraph3D.virtual = true CreateFrontEndObject[ForceGraph3D[tagsGraph]]